package strategy

import (
	"context"
	"gitee.com/lipore/plume/auth"
	"gitee.com/lipore/plume/db_gorm"
	"gitee.com/lipore/plume/errors"
	"gitee.com/lipore/plume/logger"
	"golang.org/x/crypto/bcrypt"
	"gorm.io/gorm"
	"time"
)

type Model struct {
	ModelTime
	ID uint64 `gorm:"primaryKey" json:"id"`
}

type ModelTime struct {
	CreatedAt time.Time `gorm:"autoUpdateTime" json:"-"`
	UpdatedAt time.Time `gorm:"autoCreateTime" json:"-"`
}

type Role struct {
	Model
	Name     string
	Accesses []Access `gorm:"many2many:role_access"`
}

type Access struct {
	Model
	Name string
	Code string
}

type User struct {
	gorm.Model
	UserAccount string `gorm:"column:account"`
	Password    string
	UserName    string `gorm:"column:name"`
	Roles       []Role `gorm:"many2many:user_role"`
}

func (u *User) Name() string {
	return u.UserName
}

func (u *User) Account() string {
	return u.UserAccount
}

func (u *User) VerifyPassword(password string) bool {
	encryptedPassword, _ := bcrypt.GenerateFromPassword([]byte(password), 10)
	logger.Debugf("password: %d", string(encryptedPassword))
	return bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password)) == nil
}

func (u *User) Id() interface{} {
	return u.ID
}

func (u *User) Accesses() []auth.Access {
	accessMap := make(map[auth.Access]bool)
	for _, role := range u.Roles {
		for _, access := range role.Accesses {
			accessMap[auth.Access(access.Name)] = true
		}
	}
	accesses := make([]auth.Access, 0)
	for access := range accessMap {
		accesses = append(accesses, access)
	}
	return accesses
}

type dataSource struct {
}

func NewAuthenticateDataSource() auth.DataSource {
	return &dataSource{}
}

func (a *dataSource) FetchUser(ctx context.Context, userAccount string) auth.User {

	u := User{}
	db_gorm.WithTx(ctx, func(tx *gorm.DB) error {
		err := tx.Preload("Roles").Preload("Roles.Accesses").Find(&u, "account = ?", userAccount).Error
		if err != nil {
			logger.Warnf("%v", errors.WithMessage(err, "can't found user"))
			return err
		}
		return nil
	})
	return &u
}

func InitRepository(ctx context.Context) {
	err := db_gorm.WithTx(ctx, func(tx *gorm.DB) error {
		return tx.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
			return tx.AutoMigrate(&User{}, &Role{}, &Access{})
		})
	})
	if err != nil {
		logger.Errorf("%v", err)
		panic(err)
	}
}

func init() {
}
